/*
 *  cmd_handler.c
 *  peertopeer
 *
 *  Created by jihed kamkoum on 09/05/10.
 *  Copyright 2010 ENSEIRB. All rights reserved.
 *
 */
#include "cmd_handler.h"


void create_serveur_sock(SOCKET * sock){
	/* Création d'une socket */

	*sock = socket(AF_INET, SOCK_STREAM, 0);
	if(*sock == INVALID_SOCKET){
		perror("socket");
		exit(EXIT_FAILURE);
	}
	/* Si la socket est valide */
	printf("La socket %d est maintenant ouverte en mode TCP/IP\n", *sock);
}
void sock_puts(int sock,const char* message){
	char buffer[BUFSIZ];
	memset((char *) &buffer, 0, sizeof(buffer));
	strcpy(buffer,message);
	buffer[strlen(message)+1]='\0';
	if ((send(sock, buffer, BUFSIZ, 0)==-1)) {
		perror("send");
	}
}
void cmd_raw_handler(cmdrecv * recived){
	char ip[TAILLE_IP];
	int port;
	char cmd[NAMESIZE];
	char rawcmd[NAMESIZE];
	int fd;
	if((fd=open("raw.temp", O_RDWR|O_CREAT,0777))==-1){
		perror("open");
		exit(EXIT_FAILURE);
	}
	dup2(1, 7);
	dup2(fd, STDOUT_FILENO);
	sscanf(recived->cmd,"%s %s : %d %s ",rawcmd,ip,&port,cmd);
	if (strcmp(cmd, "set") == 0) {
		cmd_set(NULL,&recived->ip_s,&recived->port_s,&recived->path);
	}
	else if(strcmp(cmd, "upload") == 0){
		pthread_mutex_lock(recived->uploadTableAccess);
		afficher_listeUpload(recived->listeUpload);
		pthread_mutex_unlock(recived->uploadTableAccess);				
	}
	else if(strcmp(cmd, "download") == 0){
		pthread_mutex_lock(recived->downloadTableAccess);
		afficher_listeDownload(recived->listeDownload);
		pthread_mutex_unlock(recived->downloadTableAccess);
	}
	close(fd);
	dup2(7, 1);
	//reouvrir
	if((fd=open("raw.temp", O_RDWR|O_CREAT,0777))==-1){
		perror("open");
		exit(EXIT_FAILURE);
	}
	char reponce[BUFSIZ];
	
	if ((read(fd,reponce, BUFSIZ))==-1) {
		perror("read");
		exit(EXIT_FAILURE);
	}
	else {
		sock_puts(recived->sock, reponce);
	}
	close(fd);
	if ((remove("raw.temp")==-1)) {
		perror("remove");
	}
	
}
void cmd_checksum_handler(cmdrecv * recived){
	int taille_block;
	char cmd[NAMESIZE];
	char key[100];
	int debut;
	int fin;
	sscanf(recived->cmd,"%s %s %d %d %d",cmd,key,&taille_block,&debut,&fin);
	calculemd5(recived->semid,recived->path, recived->listeFichiers,recived->fileTableAccess,recived->ip_s,recived->port_s);
	if (recived->listeFichiers != NULL){
		char *name = fgetname(recived->listeFichiers, key);
		char *pathfile = fpath(name, recived->path);
		vector_p v = fmd5sum_bloc(pathfile, debut, fin, taille_block);
		int i, nb_sum = vector_len(v);
		char checksum [nb_sum][BUFSIZE];
		for(i=0 ; i<nb_sum; i++)
			memset((char *) &checksum[i], 0, sizeof(checksum[i]));
		for(i=0 ; i<nb_sum; i++)
			strcpy(checksum[i], vector_get(v, i));
		for(i=0; i<nb_sum; i++){
			char buffer[BUFSIZ];
			memset((char *) &buffer, 0, sizeof(buffer));
			sprintf(buffer, "checksum %d : ",i+1);
			strcat(buffer,checksum[i]);

			buffer[strlen(checksum[i])+1]='\0';
			pthread_mutex_lock(recived->clientSocketAccess);
			sock_puts(recived->sock, buffer);
			pthread_mutex_unlock(recived->clientSocketAccess);
			sched_yield();
		}

		free(pathfile);

	}


}
void cmd_get_handler(cmdrecv * recived){
	int begin,end;
	char name[NAMESIZE];
	bzero((char *)name, NAMESIZE);
	char key[100];
	bzero((char *)key, 100);
	sscanf(recived->cmd,"%s %s %d %d",name,key,&begin,&end);
	file_t * file;
	int delay = 0;
	int port = getRoundPort(recived->ip_s);
	char ready[BUFSIZ];
	//debut section critique
	int nbbloack;
	upload *up;
	pthread_mutex_lock(recived->fileTableAccess);
	if ((file=findElementByKey2(recived->listeFichiers,(u_char*)key,recived->port_s,recived->ip_s))!=NULL) {
		sprintf(ready,"getting %s %d %s %d TCP %d %d",key,delay,recived->ip_s,port,begin,end);
		pthread_mutex_lock(recived->clientSocketAccess);
		sock_puts(recived->sock, ready);
		pthread_mutex_unlock(recived->clientSocketAccess);
		up = malloc(sizeof(upload));
		up->port = port;
		up->delay = delay;
		up->pourcentage = 0;
		up->debut = begin;
		up->fin = end;
		strcpy(up->ip ,recived->ip_s);
		strcpy(up->key,(char*)file->key);

		if (end>file->size) {
			nbbloack = floorf(file->size/BUFSIZ)+1;
		}
		else {
			nbbloack = floorf((end-begin)/BUFSIZ)+1;
		}

		up->taille = nbbloack;
		pthread_mutex_lock(recived->uploadTableAccess);
		insererUpload(recived->listeUpload,up);
		pthread_mutex_unlock(recived->uploadTableAccess);
		sched_yield();
		pthread_mutex_unlock(recived->fileTableAccess);
		//ouvrire le fichier en mode lecture

		int newsock;
		create_serveur_sock(&newsock);
		int reuse_addr = 1;
		if ((setsockopt(newsock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr,sizeof(reuse_addr))==-1)) {
			perror("setsockopt");
		}
		SOCKADDR_IN sin;
		bzero((char*)&sin, sizeof(SOCKADDR_IN));
		socklen_t recsize = sizeof(sin);
		/* Configuration */
		sin.sin_addr.s_addr = inet_addr(recived->ip_s);
		printf("%s\n",inet_ntoa(sin.sin_addr));
		sin.sin_family = AF_INET;                 /* Protocole familial (IP) */
		sin.sin_port = htons(port);               /* Listage du port */
		if((bind(newsock, (SOCKADDR*)&sin, recsize))== SOCKET_ERROR){
			perror("Bind");
			close(newsock);
			exit(EXIT_FAILURE);
		}

		if((listen(newsock, FDMAX))== SOCKET_ERROR){
			perror("listen");
		}
		int connection;
		SOCKADDR_IN csin;
		socklen_t crecsize = sizeof(csin);
		connection = accept(newsock, (SOCKADDR*)&csin, &crecsize);
		if (connection < 0) {
			perror("accept");
			exit(EXIT_FAILURE);
		}
		char nblockstring[NAMESIZE];
		memset((char *) &nblockstring, '\0', sizeof(nblockstring));
		sprintf(nblockstring,"%d",nbbloack);
		if ((send(connection, nblockstring, NAMESIZE, 0)==-1)) {
			perror("send");
		}
		char readed[BUFSIZ];
		int fdopen;
		if ((fdopen= open(fpath((const char *)file->name,recived->path),O_RDONLY))==-1) {
			perror("open");
			exit(EXIT_FAILURE);
		}
		while (nbbloack>0) {
			if ((pread(fdopen,readed, BUFSIZ,begin))==-1) {
				perror("pread");
				exit(EXIT_FAILURE);
			}
			pthread_mutex_lock(recived->uploadTableAccess);
			up->pourcentage++;
			modify_upload(recived->listeUpload,up,up->pourcentage);
			pthread_mutex_unlock(recived->uploadTableAccess);
			sock_puts(connection, readed);
			nbbloack--;
			sleep(1);
		}
		close(connection);
		pthread_mutex_lock(recived->uploadTableAccess);
		supprimerUpload(recived->listeUpload,*up);
		pthread_mutex_unlock(recived->uploadTableAccess);
		//TODO : il faut supprimer le upload
	}
	else if((file=findElementByKey(recived->listeFichiers,(u_char*)key))!=NULL) {
		sprintf(ready,"redirect %s %s  : %d ",key,file->ip,file->port_s);
		sock_puts(recived->sock, ready);
	}



}
int testMotif(filelist_t * listeFichiers,pthread_mutex_t * fileTableAccess,const char * motif){
	int nb = 1;
	if (listeFichiers->size!=0) {
		pthread_mutex_lock(fileTableAccess);
		element *tmp_elt = listeFichiers->first;
		while (tmp_elt != listeFichiers->last){
			if (strstr(tmp_elt->data->name, motif)!=NULL) {
				nb++;
			}
			tmp_elt = tmp_elt->next;
		}
		pthread_mutex_unlock(fileTableAccess);
	}
	return nb;

}
void cmd_list_handler(cmdrecv* recived){
	if ((recived->listeFichiers)->size!=0){
		char cmdName[NAMESIZE];
		char motif[100];
		memset((char *) &motif, '\0', sizeof(motif));
		sscanf(recived->cmd,"%s%s",cmdName,motif);
		//sans motif
		if (strcmp(motif, "")==0) {
			char buffer2[BUFSIZ];
			memset((char *) &buffer2, '\0', sizeof(buffer2));
			//debut section critique
			pthread_mutex_lock(recived->fileTableAccess);
			element *tmp_elt = recived->listeFichiers->first;
			while (tmp_elt != recived->listeFichiers->last){
				sprintf(buffer2,"file %s %s %d %s : %d",tmp_elt->data->name,tmp_elt->data->key,tmp_elt->data->size,tmp_elt->data->ip,tmp_elt->data->port_s);
				pthread_mutex_lock(recived->clientSocketAccess);
				sock_puts(recived->sock,buffer2);
				pthread_mutex_unlock(recived->clientSocketAccess);
				sched_yield();
				tmp_elt = tmp_elt->next;
			}
			pthread_mutex_unlock(recived->fileTableAccess);
			//fin section critique
			sprintf(buffer2,"end_file");
			pthread_mutex_lock(recived->clientSocketAccess);
			sock_puts(recived->sock,buffer2);
			pthread_mutex_unlock(recived->clientSocketAccess);

		}//avec motif
		else {
			char buffer2[BUFSIZ];
			memset((char *) &buffer2, '\0', sizeof(buffer2));
			//debut section critique
			pthread_mutex_lock(recived->fileTableAccess);
			element *tmp_elt = recived->listeFichiers->first;
			while (tmp_elt != recived->listeFichiers->last){
				if (strstr((const char *)tmp_elt->data->name, motif)!=NULL) {
					sprintf(buffer2,"file %s %s %d %s : %d",tmp_elt->data->name,tmp_elt->data->key,tmp_elt->data->size,tmp_elt->data->ip,tmp_elt->data->port_s);
					pthread_mutex_lock(recived->clientSocketAccess);
					sock_puts(recived->sock,buffer2);
					pthread_mutex_unlock(recived->clientSocketAccess);
				}
				sched_yield();
				tmp_elt = tmp_elt->next;
			}
			pthread_mutex_unlock(recived->fileTableAccess);
			//fin section critique

		}

	}
}
void cmd_traffic_handler(cmdrecv* recived){
	if ((recived->listeUpload)->taille!=0){
		char buffer2[BUFSIZ];
		memset((char *) &buffer2, '\0', sizeof(buffer2));
		//debut section critique
		pthread_mutex_lock(recived->uploadTableAccess);
		EltListeUpload *elt;
		elt=malloc(sizeof(EltListeUpload));
		elt=(recived->listeUpload)->fin;
		while(elt!=NULL){
			sprintf(buffer2,"ready %s %d %s %d TCP %d %d",elt->donnees.key,elt->donnees.delay,elt->donnees.ip,elt->donnees.port,elt->donnees.debut,elt->donnees.fin);
			pthread_mutex_lock(recived->clientSocketAccess);
			sock_puts(recived->sock,buffer2);
			pthread_mutex_unlock(recived->clientSocketAccess);
			elt=elt->precedent;
			sched_yield();

		}
		sprintf(buffer2,"end_traffic");
		pthread_mutex_lock(recived->clientSocketAccess);
		sock_puts(recived->sock,buffer2);
		pthread_mutex_unlock(recived->clientSocketAccess);
		pthread_mutex_unlock(recived->uploadTableAccess);
	}
	//fin section critique
	
}
void cmd_neighbourhoud_handler(cmdrecv* recived){
	if ((recived->listeClients)->size!=0){
		elementclient *elt;
		char buffer2[BUFSIZ];
		memset((char *) &buffer2, '\0', sizeof(buffer2));
		//debut section critique
		pthread_mutex_lock(recived->clientTableAccess);
		elt=malloc(sizeof(elementclient));
		elt=(recived->listeClients)->first;
		while(elt!=NULL){
			sprintf(buffer2,"neighbour %s : %d",(elt->data)->ip,(elt->data)->port_s);
			pthread_mutex_lock(recived->clientSocketAccess);
			sock_puts(recived->sock,buffer2);
			pthread_mutex_unlock(recived->clientSocketAccess);
			sched_yield();
			elt=elt->next;
		}
		sprintf(buffer2,"end_neighbour");
		pthread_mutex_lock(recived->clientSocketAccess);
		sock_puts(recived->sock,buffer2);
		pthread_mutex_unlock(recived->clientSocketAccess);
		//fin section critique
		
	}
}
void * f_thread_md5computer(md5computer * compute){
	printf("debut thread %d\n",pthread_self());
	sleep(rand()%3);
	//debut section critique
	pthread_mutex_lock(compute->fileTableAccess);
	//delete the last list

	finitlist(compute->listeFichiers,compute->path,compute->port,compute->ip);
	flist_print(compute->listeFichiers);
	pthread_mutex_unlock(compute->fileTableAccess);
	// fin section critique
	/*Liberation d'un thread client*/
	struct sembuf op;
	op.sem_num = 0; //Test si le thread est dispo
	op.sem_op = 1; //V()
	op.sem_flg = 0; //aucune importance
	if((semop(compute->semid, &op, 1)==-1)){
		perror("semop V()");
	}
	printf("fin thread %d\n",pthread_self());
	pthread_exit(NULL);
	
}
void calculemd5(int semid,char * path,filelist_t * listeFichiers,pthread_mutex_t * fileTableAccess,char * ip,int port){
	struct sembuf op;
	op.sem_num = 0; //Test si le thread est dispo
	op.sem_op = -1; //P()
	op.sem_flg = 0; //attente de l'operation
	if((semop(semid, &op, 1)==0)){
		pthread_t threadMd5;
		md5computer * compute = malloc(sizeof(cmdrecv));
		compute->path = path;
		compute->semid = semid;
		strcpy(compute->ip,ip);
		compute->port = port;
		compute->fileTableAccess = fileTableAccess;
		compute->listeFichiers = listeFichiers;
		if((pthread_create(&threadMd5, NULL,(void *)f_thread_md5computer,compute))==-1)
			printf("probleme pthread_create \n");
		else {
			changer_ordo_priorite(threadMd5, SCHED_RR, 5);
		}
	}
	else{
		perror("semop P()");
	}		
}
void timeout_handler(int *connectlist,clientlist_t * listeClients, float time_out){
	int listnum;
	float duration;
	for (listnum=0; listnum<FDMAX;listnum++) {
		client_t * temp ;
		if((temp = findBySocket(connectlist[listnum], listeClients))!=NULL){
			clock_t last_event = (findBySocket(connectlist[listnum], listeClients))->last_event;
			clock_t now = clock();
			duration = 10000*((float)now - last_event)/CLOCKS_PER_SEC;
			if (duration>time_out) {
				//TODO : verrou
				clientlist_remove(listeClients, temp);
				sock_puts(connectlist[listnum], "connection cancled \n");
				connectlist[listnum] = 0;
			}
		}
	}
	
	
}
int getRoundPort(char * ip){
	int port = 1025+((int)pthread_self()/1000);
	int sock_err=-1;
	int  sock;
	while (sock_err==-1) {
		create_serveur_sock(&sock);
		int reuse_addr = 1;
		if ((setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr,sizeof(reuse_addr))==-1)) {
			perror("setsockopt");
		}
		SOCKADDR_IN sin;
		bzero((char*)&sin, sizeof(SOCKADDR_IN));
		socklen_t recsize = sizeof(sin);
		/* Configuration */
		sin.sin_addr.s_addr = inet_addr(ip);
		printf("%s\n",inet_ntoa(sin.sin_addr));
		sin.sin_family = AF_INET;                 /* Protocole familial (IP) */
		sin.sin_port = htons(port);               /* Listage du port */
		if((sock_err = bind(sock, (SOCKADDR*)&sin, recsize))== SOCKET_ERROR){
			perror("Bind");
			port = port - (int)time(NULL);
			close(sock);
		}
	}
	close(sock);
	return port;
}
